#!/usr/bin/python

import os
import sys

CONFIG_FILE_NAME="config.txt"
SCRIPT_PATH=sys.path[0]

EXCLUDE_PACKAGES=[
#	"sun/misc/",
#	"java/security/",
#	"java/lang/reflect/",
	"sun/reflect/"
]

EXCLUDE_CLASSES=[
#	"sun/misc/URLClassPath\$3",
#	"sun/misc/URLClassPath\$Loader",
#	"sun/misc/URLClassPath\$JarLoader",
#	"java/net/URLClassLoader",
#	"java/net/URLClassLoader\$1",
#	"java/lang/ClassNotFoundException",
#	"sun/misc/URLClassPath\$JarLoader\$1",
	"site$py",
	"os$py"
]

LIB_PATH=os.path.join(os.path.join(SCRIPT_PATH,"lib"),"libdacapoagent.so").replace("\\","/")

AGENT_BASE="-agentpath:"+LIB_PATH+"="
# +SCRIPT_PATH

AGENT_BASE_OPTIONS=[
   "base="+os.path.join(SCRIPT_PATH,"dist").replace("\\","/"),
   "exclude_package="+";".join(EXCLUDE_PACKAGES),
   "exclude_classes="+";".join(EXCLUDE_CLASSES),
   "log_start=org/dacapo/harness/Benchmark.startIteration()V",
   "log_stop=org/dacapo/harness/Benchmark.stopIteration()V"
]

TEST="test"
CONFIG="make"
AGENT="agent"
DESCRIPTION="desc"
RESULT="result"
NEXT="next"
DEFAULT="default"

# vm command line arguments
VM_HEAP="-heap"

# command line argument options
OPT_STORE="-store"
OPT_ALLOCATE="-allocate"
OPT_POINTER="-pointer"
OPT_MONITOR="-monitor"
OPT_EXCEPTION="-exception"
OPT_THREAD="-thread"
OPT_METHOD="-method"
OPT_LOG_DIR="-log_dir"
OPT_LOG_LIMIT="-limit"
OPT_DACAPO_JAR="-dacapo"
OPT_HELP="-help"
OPT_DONT_EXECUTE="-no"
OPT_GZIP="-gzip"
OPT_AGENT_DIR="-agent_dir"

DEFAULT_LOG_DIR=None
DEFAULT_DACAPO_JAR="dacapo.jar"

# AGENT_JAR_FILES=["dist/agent.jar"]
AGENT_JAR_FILES=[os.path.join(SCRIPT_PATH,"agentboot")]

doExecute = True

VM_ARGS={
  VM_HEAP:{
    TEST: lambda x: { RESULT:x!="", NEXT:None },
    CONFIG: lambda b,x: ["-Xms"+x[0],"-Xmx"+x[0]],
    DESCRIPTION: " set VM heap size"
  }
}

CONFIG_ARGS={
  OPT_GZIP:{
    TEST: None,
    CONFIG: lambda b,x: ["gzip="],
    DESCRIPTION: " gzip log files",
    DEFAULT: []
  },
  OPT_STORE:{
    TEST: lambda x: { RESULT:x=="tx",NEXT:None },
    CONFIG: lambda b,x: ["store=tx" if "tx" in x else "store="],
    DESCRIPTION: " [tx] create temporary store for loaded classes",
    DEFAULT: []
  },
  OPT_ALLOCATE:{
    TEST: lambda x: { RESULT:x.isdigit(), NEXT:None},
    CONFIG: lambda b,x: ["load_classes=","allocate=java/lang/ClassNotFoundException;java/lang/NoClassDefFoundError"] if x==[] else ["load_classes","allocate=java/net/URLClassLoader$1;java/net/URLClassLoader;java/lang/ClassNotFoundException;java/lang/NoClassDefFoundError","gc="+x[0]],
    DESCRIPTION: " [<byte>]\n  where <bytes> is the amount of bytes for gc to be forced",
    DEFAULT: []
  },
  OPT_POINTER:{
  	TEST: None,
  	CONFIG: lambda b,x: ["pointer="],
  	DESCRIPTION: " collect pointer data",
    DEFAULT: []
  },
  OPT_MONITOR:{
    TEST: None,
    CONFIG: lambda b,x: ["monitor="],
    DESCRIPTION: " collect monitor events",
    DEFAULT: []
  },
  OPT_EXCEPTION:{
    TEST: None,
    CONFIG: lambda b,x: ["exception="],
    DESCRIPTION: " collect exception events",
    DEFAULT: []
  },
  OPT_LOG_LIMIT:{
    TEST: None,
    CONFIG: lambda b,x: ["log_limit="],
    DESCRIPTION: " limit log file size splitting the log",
    DEFAULT: []
  },
  OPT_THREAD:{
    TEST: None,
    CONFIG: lambda b,x: ["thread="], 
    DESCRIPTION: " collect thread events",
    DEFAULT: []
  },
  OPT_METHOD:{
    TEST: None,
    CONFIG: lambda b,x: ["method_events="], 
    DESCRIPTION: " collect method events",
    DEFAULT: []
  },
  OPT_LOG_DIR:{
    TEST: lambda x: { RESULT:x, NEXT:None },
    CONFIG: lambda b,x: ["log_file="+b+".csv" if x is None else "log_file="+x[0]+"/"+b+".csv"],
    DESCRIPTION: " <log_dir> \n  where <log_dir>/<bm>-0.csv will be the first log file.",
    DEFAULT: []
  },
  OPT_DACAPO_JAR:{
    TEST: lambda x: { RESULT:x, NEXT:None },
    CONFIG: lambda b,x: [],
    DESCRIPTION: " <dacapo-jar-file> to use",
    DEFAULT: [DEFAULT_DACAPO_JAR]
  }
}

AGENT_ARGS={
  OPT_AGENT_DIR:{
  	TEST: lambda x: { RESULT:x, NEXT:None },
  	CONFIG: lambda b,x: [b],
  	DESCRIPTION: " <dacapo-agent-working-directory>",
  	DEFAULT: [os.path.join(os.getcwd(),"working")]
  }
}

def reportHelpAndExit():
  for opt in CONFIG_ARGS.keys():
    print opt + ": " + CONFIG_ARGS[opt][DESCRIPTION] + "\n"
  exit(10)

def processCommandLine(args):
  agentOptions={}
  configOptions={}
  vmOptions={}
  benchmarks=[]
  doExecute=True
  if args==None or args==[]:
    return (agentOptions,configOptions,vmOptions,benchmarks,doExecute)
  while args!=[]:
    arg = args[0]
    args=args[1:]
    if arg is not None:
      if arg.startswith("--"):
        benchmarks.append(arg[2:])
      elif arg in CONFIG_ARGS.keys():
        opt=CONFIG_ARGS[arg]
        test=opt[TEST]
        subArgs=[]
        if test:
          while args!=[] and test and test(args[0])[RESULT]:
            subArg=args[0]
            args=args[1:]
            subArgs.append(subArg)
            test = test(subArg)[NEXT]
        configOptions[arg]=subArgs
      elif arg in AGENT_ARGS.keys():
        opt=AGENT_ARGS[arg]
        test=opt[TEST]
        subArgs=[]
        if test:
          while args!=[] and test and test(args[0])[RESULT]:
            subArg=args[0]
            args=args[1:]
            subArgs.append(subArg)
            test = test(subArg)[NEXT]
        agentOptions[arg]=subArgs
      elif arg in VM_ARGS.keys():
        opt=VM_ARGS[arg]
        test=opt[TEST]
        subArgs=[]
        if test:
          while args!=[] and test and test(args[0])[RESULT]:
            subArg=args[0]
            args=args[1:]
            subArgs.append(subArg)
            test = test(subArg)[NEXT]
        vmOptions[arg]=subArgs
      elif arg == OPT_HELP: 
        reportHelpAndExit()
      elif arg == OPT_DONT_EXECUTE:
        doExecute=False
      elif arg.startswith("-"):
        return None
      else:
        benchmarks.append(arg)
  return (agentOptions,configOptions,vmOptions,benchmarks,doExecute)

def reportErrorExit(m):
  print m
  exit(10)

def fold_r(list,f,v):
  if list == None or list == []:
    return v
  return f(list[0],fold_r(list[1:],f,v))

result=processCommandLine(sys.argv[1:])

if result is None:
  print "Argument error"
  exit(10)

(agentOptions,configOptions,vmOptions,benchmarks,doExecute)=result

# ../dacapo-20100503.jar

jarFiles = []
if OPT_DACAPO_JAR in configOptions.keys():
  jarFiles = configOptions[OPT_DACAPO_JAR]
else:
  jarFiles = [DEFAULT_DACAPO_JAR]

if not fold_r(jarFiles, lambda x,y: y and os.path.exists(x), True):
  reportErrorExit("unable to find all the jar files: " + ", ".join(jarFiles))

agentDir = AGENT_ARGS[OPT_AGENT_DIR][DEFAULT][0]
if OPT_AGENT_DIR in agentOptions.keys():
  agentDir = agentOptions[OPT_AGENT_DIR][0]
agentDir = os.path.abspath(agentDir)
if not os.path.exists(agentDir):
  os.makedirs(agentDir)
    
if OPT_LOG_DIR not in configOptions.keys():
  configOptions[OPT_LOG_DIR]=DEFAULT_LOG_DIR

for bm in benchmarks:
  command_line_opts=AGENT_BASE_OPTIONS
  vm_opts=["java","-Xbootclasspath/a:" + ":".join(AGENT_JAR_FILES)]# agent.jar"]
  
  for arg in configOptions.keys():
    command_line_opts=command_line_opts+CONFIG_ARGS[arg][CONFIG](bm,configOptions[arg])

  with open(os.path.join(agentDir, CONFIG_FILE_NAME), 'w') as f:
    for arg in command_line_opts:
      print >>f, arg

  for arg in vmOptions.keys():
    vm_opts=vm_opts+VM_ARGS[arg][CONFIG](bm,vmOptions[arg])

  command = " ".join(vm_opts) + " -classpath " + ":".join(jarFiles) + " \"" + AGENT_BASE + agentDir + "\" Harness " + bm

  print command

  if doExecute:
    os.system(command)
